#pragma once

#include "znx_event.h"
#include "znx_event_loop.h"
#include "comm/znx_log.h"
#include "comm/znx_queue.h"


typedef enum {
    ZNX_AIO_READ            = 1,
    ZNX_AIO_WRITE           = 2
} znx_aio_opcode_t;


typedef struct {
    znx_aio_opcode_t        op;

    int                     fd;
    void                    *buf;
    size_t                  nbytes;
    off_t                   offset;

    // context
    znx_dispatch_event_t    dispatch_event;

    znx_result_t            ret;
} znx_aio_request_t;


static inline void
znx_aio_reqeuest_init(
    znx_aio_request_t *req,
    znx_aio_opcode_t op,
    int fd, void *buf, size_t nbytes, off_t offset,
    znx_dispatch_handler_t handler, void *data)
{
    bzero(req, sizeof(znx_aio_request_t));

    req->op = op;
    req->fd = fd;
    req->buf = buf;
    req->nbytes = nbytes;
    req->offset = offset;

    req->dispatch_event.handler = handler;
    req->dispatch_event.data = data;
    req->dispatch_event.save_data_1 = req;
}


typedef struct {
    znx_mutex_t             mutex;
    znx_cond_t              cond;
    znx_queue_t             extern_queue;
    znx_atomic_int64_t      tasks;
    znx_bool_t              stop;
} znx_aio_loop_data_t;


static inline void
znx_aio_loop_data_init(znx_aio_loop_data_t *loop_data)
{
    bzero(loop_data, sizeof(znx_aio_loop_data_t));
    znx_mutex_init(&loop_data->mutex);
    znx_cond_init(&loop_data->cond);
    znx_queue_init(&loop_data->extern_queue);
}


typedef struct {
    int                     n_read_threads;
    int                     n_write_threads;

    znx_aio_loop_data_t     read_loop_data;
    znx_aio_loop_data_t     write_loop_data;

    znx_thread_base_t       *read_threads;
    znx_thread_base_t       *write_threads;
} znx_aio_manager_t;


void znx_aio_manager_start(znx_aio_manager_t *aio_manager,
    int n_read_threads, int n_write_threads, const char *tag);

void znx_aio_manager_stop(znx_aio_manager_t *aio_manager);

// the following interfaces can only be called in the worker thread.
void znx_aio_post_event(znx_aio_manager_t *aio_manager,
    znx_aio_request_t *req);


static inline int64_t
znx_aio_manager_read_tasks(znx_aio_manager_t *aio_manager) {
    return znx_atomic_int64_load(&aio_manager->read_loop_data.tasks);
}


static inline int64_t
znx_aio_manager_write_tasks(znx_aio_manager_t *aio_manager) {
    return znx_atomic_int64_load(&aio_manager->write_loop_data.tasks);
}


extern char *znx_aio_thread_tag;

static inline znx_bool_t
current_thread_is_aio_thread()
{
    if (current_thread_base == NULL) {
        return ZNX_FALSE;
    }

    if (current_thread_base->tag != znx_aio_thread_tag) {
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}
